#include "gdbmanager.h"
#include "mainwindow.h"
#include "eventhandler.h"
#include <string>
#include <QTextCodec>
#include <QObject>
#include <QMessageBox>
#include <QTime>

#ifndef Q_OS_WIN
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <vector>
#include <dirent.h>
#if defined(__linux__) || defined(_HPUX_SOURCE) || defined(SOLARIS)
#include <fstream>
#elif defined(AIX)
#include <fcntl.h>
#include <sys/procfs.h>
#endif
#ifdef SOLARIS
#include <signal.h>
#endif
#endif

#ifdef Q_OS_WIN
#define WIN32_MEAN_AND_LEAN
#include <windows.h>
#include <tlhelp32.h>
#undef CONST    // conflict with CONST in MIValueType
#undef ERROR
#endif

#define IS_RESULT_RECORD(r, name)    ((r) && (r)->m_class == RC_DONE && (r)->m_pResult && \
                                    strcmp ((r)->m_pResult->m_variable, name) == 0)
#define IS_RESULT_ERROR(r)            (! (r) || (r)->m_class == RC_ERROR)

#define REMOVE_ITEM_CHILD(item)        do { \
    int _cnt = (item)->childCount (); \
    while (_cnt --) \
        delete (item)->child (0); \
    } while (0)

#define ARRAY_EAGE 500 // length that array need to show partly.

// For convenience, this moudule needs MainWindow to be global seen,
// this will be initialized in GDBMIDefaultClient;
static MainWindow *s_mainWindow = 0;
static bool isStackChanged = false;
static QTime time_ticker;

#ifndef Q_OS_WIN
using std::string;

static sighandler_t        s_handler    = 0;
static int                s_xtermPid    = 0;

pid_t createXTerm(const char * program, QString & ttyName)
{
    char    fifoName[64];

    sprintf (fifoName, "/tmp/MannaDebug%05d", getpid ());
    unlink(fifoName);    // Remove remnants

    // Create a fifo that will pass in the tty name
#ifdef HAVE_MKFIFO
    if (mkfifo(fifoName, S_IRUSR|S_IWUSR) < 0)
        return -1;
#else
    if (mknod(fifoName, S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0)
        return -1;
#endif
    int pidXterm = fork();
    if (pidXterm == 0)
    {
        // Child context
        const string shellScript(
            string ("tty>") + string (fifoName) + ";"
            + "trap \"\" INT QUIT TSTP;"   // Ignore various signals
            + "exec<&-;exec>&-;"           // Close stdin and stdout
            + "while :;do sleep 3600;done"
            );
        const string title = string ("Manna: ") + string (program);
        const char *argv[16];
        unsigned ax(0);
        argv[ax++] = "xterm";
        argv[ax++] = "-name";
        argv[ax++] = "GUIDE";
        argv[ax++] = "-title";
        argv[ax++] = title.c_str ();
        argv[ax++] = "-font";
        argv[ax++] = "8x15";
        //argv[ax++] = "-bg";
        //argv[ax++] = "black";
        argv[ax++] = "-e";
        argv[ax++] = "sh";
        argv[ax++] = "-c";
        argv[ax++] = shellScript.c_str();
        argv[ax++] = 0;
        execvp (argv[0], (char * const *)argv);
        // We get here only if exec failed
        //lg1(("fork failed for fifo " + fifoName).c_str());
        _exit (127);
    }
    // Parent context. Read ttyname from fifo
    int fd = open (fifoName, O_RDONLY);
    if (fd >= 0)
    {
        char buf[256];
        const int numRead = read (fd, buf, sizeof (buf) - sizeof (char)); // Leave space for '\0'
        close (fd);

        if (numRead >= 0)
        {
            // Remove whitespace
            buf[numRead] = '\0';
            ttyName = QString (buf).trimmed ();
        }
    }
    unlink (fifoName);

    return pidXterm;
}

void onXTermExit (int signum)
{
    if (signum == SIGCHLD && s_mainWindow)
    {
        MEventTargetQuit e;
        QApplication::sendEvent (s_mainWindow, & e);
    }

    if (s_handler && s_handler != SIG_IGN && s_handler != SIG_DFL)
        (* s_handler) (signum);
}
#endif // ! Q_OS_WIN

#ifdef Q_OS_WIN
class Win32DebugThread : public QThread
{
public:
    Win32DebugThread (const QString & targetName, unsigned long gdbPid) : m_gdbPid (gdbPid)
    {
        QString name = targetName.section ('\\', -1);
        m_lpszTarget = new TCHAR[name.length () + 1];
#ifdef UNICODE
        * (m_lpszTarget + name.toWCharArray (m_lpszTarget)) = 0;
#else
        strcpy (m_lpszTarget, name.toLatin1 ().constData ());
#endif
    }
    ~Win32DebugThread ()
    {
        delete [] m_lpszTarget;
    }
protected:
    virtual void run ()
    {
        HANDLE hProcess = 0;
        int count = 0;//when count is large than 10,it means that the target process was not found in 10s.
        while ((hProcess = findProcess ()) == 0) {
            count ++;
            if (count >= 10)break;
            msleep (200);
        }
 
        if (count < 10) {
            WaitForSingleObject (hProcess, INFINITE);
            CloseHandle (hProcess);
        }

        // Should post this event, otherwise Manna will crash.
        // The reason is, unfortunately, unkown.
        MEventTargetQuit * e = new MEventTargetQuit;
        QApplication::postEvent (s_mainWindow, e);
    }

    HANDLE findProcess ()
    {
        HANDLE            hProcess = NULL;
        HANDLE            hProcessSnap = NULL;
        BOOL            bFind      = FALSE;
        PROCESSENTRY32    pe32      = {0};

        hProcessSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);

        if ((hProcessSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0))
                == INVALID_HANDLE_VALUE)
            return 0;

        pe32.dwSize = sizeof (PROCESSENTRY32);
        //OutputDebugString (TEXT("It is me"));
        OutputDebugString (m_lpszTarget);
        if (Process32First (hProcessSnap, & pe32))
        {
            do
            {
                if (pe32.th32ParentProcessID == m_gdbPid &&
                    lstrcmpi (m_lpszTarget, pe32.szExeFile) == 0)
                {
                    hProcess = OpenProcess (SYNCHRONIZE, FALSE, pe32.th32ProcessID);
                    break;
                }
            } while (! bFind && Process32Next (hProcessSnap, & pe32));
        }

        CloseHandle (hProcessSnap);
        return hProcess;
    }

    LPTSTR            m_lpszTarget;
    unsigned long    m_gdbPid;
};
#endif // Q_OS_WIN

GDBMIDefaultClient::GDBMIDefaultClient (GDBMI * pThread, MainWindow * mainWindow) 
        : GDBMIAsyncClient (pThread)
{
    s_mainWindow = mainWindow;
}

void GDBMIDefaultClient::onExit ()
{
    qDebug ("DefaultClientonExit");
    s_mainWindow->slotDebugFinished ();
}

void GDBMIDefaultClient::onStopped (STOPREASON)
{
    //qDebug ("onStopped");
}

void GDBMIDefaultClient::onAsyncRecord(MIAsyncRecord *asyncRecord)
{
    TRACEFUNC(onAsyncRecord);
    if (asyncRecord->m_class == STOPPED) {
        MIValue *value = GetMIValue(asyncRecord->m_pResult, "reason");
        qDebug("STOPPED");
        if (value && strcmp(value->m_string, "breakpoint-hit") == 0) {
            m_pGDBMI->broadcastStopped(SR_BKPT);
            s_mainWindow->raise();
            s_mainWindow->activateWindow();
        } else if (value && strcmp(value->m_string, "exited-normally") == 0) {
            m_pGDBMI->broadcastStopped(SR_EXIT);
            m_pGDBMI->emitMessage (QString ("Program [%1] exited normally").arg (
                m_pGDBMI->getTarget()));
        } else if (value && strcmp(value->m_string, "exited") == 0) {
            m_pGDBMI->broadcastStopped(SR_EXIT);
            m_pGDBMI->emitMessage (QString ("Program [%1] exited").arg (
                m_pGDBMI->getTarget()));
        } else if (value && strcmp(value->m_string, "signal-received") == 0) {
            handleSignal (asyncRecord->m_pResult);
            m_pGDBMI->broadcastStopped(SR_SIG);
        } else
            m_pGDBMI->broadcastStopped(SR_OTHER);

        value = GetMIValue(asyncRecord->m_pResult, "bkptno");
        int m_bkptno;
        if (value != 0) {
            m_bkptno = atoi(value->m_string);    //get the breakpoint number where gdb is stopped.
        } else {
            m_bkptno = -1;
        }
        value = GetMIValue(asyncRecord->m_pResult, "frame");
        if (value && (value = GetMIValue(value->m_pResult, "line"))) {
            s_mainWindow->onTargetStop(atoi(value->m_string),m_bkptno);
        } else {
            s_mainWindow->onTargetStop();
        }
    }
}

void GDBMIDefaultClient::onStreamRecord (MIStreamRecord * streamRecord)
{
    if (streamRecord->m_type == CONSOLE)
        m_pGDBMI->emitMessage (ConvertCString (streamRecord->m_string));
}

void GDBMIDefaultClient::onResultParseErr(int /*token*/)
{
    return;
}

bool GDBMIDefaultClient::onResultRecord (MIResultRecord * resultRecord)
{
    if (resultRecord->m_class == RC_EXIT)
    {
        //qDebug ("exit echo\n");
    }
    else if (resultRecord->m_class == RC_DONE)
    {
    }

    return false;
}

void GDBMIDefaultClient::handleSignal (MIResult * result)
{
    TRACEFUNC (handleSignal);

    MIValue * value;
    QString msg;

    if ((value = GetMIValue (result, "signal-name")) != 0)
        msg += QString (value->m_string);

    if ((value = GetMIValue (result, "signal-meaning")) != 0)
        msg += QString (": %1").arg (value->m_string);

    if (! msg.isEmpty ())
        m_pGDBMI->emitMessage (msg, GDBMI::CRITICAL);
}

bool GDBMIDefaultClient::startDebug (const QString & workDirectory, const QString & target)
{
    m_pGDBMI->setWorkDirectory (workDirectory);
    return m_pGDBMI->startDebug (target);
}


GDBMIExecClient::GDBMIExecClient (GDBMI * pThread) : 
    GDBMIClient (pThread), m_stopped (false)
{
#ifdef Q_OS_WIN
    m_helperThread = 0;
#endif
}

void GDBMIExecClient::run (bool stopAtMain, QString args)
{
#ifdef Q_OS_WIN
    sendRequest ("set new-console on");
    //sendRequest ("set target-charset CN-GB");
#else
    //sendRequest ("set target-charset UTF-8");
    QString ttyName;
    s_xtermPid = createXTerm (m_pGDBMI->getTarget ().toLocal8Bit (), ttyName);

    if (s_xtermPid > 0)
    {
        //s_execClient = this;
        s_handler = signal (SIGCHLD, onXTermExit);

        sendRequest (QString ("-inferior-tty-set %1").arg (ttyName));
    }
    else
        //qDebug ("failed to create X terminal");


#endif
    m_stopped = false;
    if (stopAtMain)
        sendRequest ("-break-insert main");
    if (!args.isEmpty())
        sendRequest (QString("-exec-arguments %1").arg(args));
    sendRequest ("-exec-run");
    isGdbStopped = false;
    isTempBrInsert = false;
    tempBrline = -1;
    m_bkpts.clear();
}

void GDBMIExecClient::onExit ()
{
#ifndef Q_OS_WIN
    if (s_xtermPid > 0)
    {
        signal (SIGCHLD, s_handler);
        s_handler = 0;

        int status;
        if (!kill (s_xtermPid, SIGKILL)) {
                waitpid (s_xtermPid, & status, 0);
                }
        s_xtermPid = 0;
    }
#else
    if (m_helperThread)
    {
        if (m_helperThread->isRunning ())
        {
            m_helperThread->wait ();
        }

        delete m_helperThread;
        m_helperThread = 0;
    }
#endif
//qDebug("ExecClientonExit");
}

void GDBMIExecClient::onResultParseErr(int /*token*/)
{
    return;
}

bool GDBMIExecClient::onResultRecord (MIResultRecord * resultRecord)
{
    TRACEFUNC(onResultRecord);
    if (resultRecord->m_class == RC_DONE)
    {
        if (resultRecord->m_pResult)
        {
            MIResult * result = resultRecord->m_pResult;
            if (strcmp (result->m_variable, "bkpt") == 0)
            {
                MIValue * value;
                int number = 0, line = 0;
                if ((value = GetMIValue (result->m_pValue->m_pResult, "number")) != 0)
                    number = atoi (value->m_string);
                if ((value = GetMIValue (result->m_pValue->m_pResult, "line")) != 0)
                    line = atoi (value->m_string);

                if (number && line)
                    m_bkpts.push_back (BKPTPAIR (line, number));
            }
        }
    }
    else if (resultRecord->m_class == RC_RUNNING)
    {
#ifdef Q_OS_WIN
        if (! m_helperThread)
        {
            //qDebug (m_pGDBMI->getTarget ().toLatin1 ().constData ());
            m_helperThread = new Win32DebugThread (
                m_pGDBMI->getTarget (), m_pGDBMI->pid ()->dwProcessId);
            m_helperThread->start ();
        }
#endif
    }
    else if (resultRecord->m_class == RC_ERROR) {
        MIResult * result = resultRecord->m_pResult;
        MIValue * value = GetMIValue (result, "msg");
        QString msg = ConvertCString(value->m_string);
        s_mainWindow->onTargetStop(msg);
        //qDebug(value->m_string);
        //qDebug("error is found!");
    }

    return false;
}

void GDBMIExecClient::onStopped (STOPREASON reason)
{
    m_stopped = true;
    if (reason == SR_EXIT) {
        if (m_pGDBMI->pid() != 0) //Sometimes(offical MinGW on Win7) gdb exits directly after program exits
            sendRequest ("-gdb-exit");
        isGdbStopped = true;
    } else {
        if (this->isTempBrInsert) {
            isTempBrInsert = false;
            setBreakpoint (this->tempBrFileName, this->tempBrline, false);
            this->tempBrFileName = "";
            this->tempBrline = -1;
        }
    }
}

bool GDBMIExecClient::setBreakpoint (
        const QString & filename, int line, bool set/* = true */, QString condition, bool changeCond)
{
    QString fileName;
    qDebug(filename.toUtf8().constData());
    int pos = filename.lastIndexOf('/',-1);
    if (pos != -1)
        fileName = filename.right(pos+1);
    else
        fileName = filename;
    if (set && condition.isEmpty())
        return sendRequest (QString ("-break-insert %1:%2").arg (fileName).arg (line)) != 0;
    else if (set && !condition.isEmpty()) {
        return sendRequest (QString("-break-insert -c %1 %2:%3")
                            .arg('\"' + condition + '\"').arg (fileName).arg (line)) != 0;
    } else if (changeCond) {
        for (std::list<BKPTPAIR>::iterator it = m_bkpts.begin ();
            it != m_bkpts.end ();
            ++ it)
        {
            if (it->first == line) {
                return sendRequest (QString ("-break-condition %1 %2")
                                          .arg(it->second).arg('\"' + condition + '\"')) != 0;
            }
        }
    }
    else
    {
        for (std::list<BKPTPAIR>::iterator it = m_bkpts.begin ();
            it != m_bkpts.end ();
            ++ it)
        {
            if (it->first == line) {
                bool flag = (sendRequest (QString ("-break-delete %1").arg (it->second)) != 0);
                m_bkpts.erase(it);
                return flag;
            }
                //return sendRequest(QString("-break-delete %1:%2").arg(fileName).arg(line)) != 0;
        }
        return false;
    }
}

int GDBMIExecClient::getUnnecessaryBreakpoints(int line,int bkptno,std::list<BKPTPAIR> &bkpt_list)
{

    int count = 0;
    int form_line = 0;
    for (std::list<BKPTPAIR>::iterator it = m_bkpts.begin();it != m_bkpts.end();++it) {
        if (it->second == bkptno) {
            if (it->first == line) {
                return 0;
            } else {
                form_line = it->first;break;
            }
        }
    }

    for (std::list<BKPTPAIR>::iterator it = m_bkpts.begin();it != m_bkpts.end();++it) {
        if (it->first >= form_line && it->first <= line) {
            bkpt_list.push_back(BKPTPAIR (it->first, it->second));
            ++count;
        }
    }

    return count;
}

bool GDBMIExecClient::breakpointExist(int line)
{

    for (std::list<BKPTPAIR>::iterator it = m_bkpts.begin();it != m_bkpts.end();++it) {
        if (it->first == line) {
            return true;
        }
    }
    return false;
}

QString GDBMIStackClient::gdbstrToUnicode(QString src)
{
    char src_char[4096],des_char[4096];
#ifdef Q_OS_WIN
    src.replace("//", "/");
    src.replace(QString("\\\\"),QString("/"));
#endif
    sprintf(src_char,"%s",src.toLocal8Bit().constData());
    int j = 0;
    for (int i = 0;i < src.length();++i) {
        if (src_char[i] != '\\') {
            des_char[j++] = src_char[i];
        } else {
            unsigned int calc = 0;
            ++i;
            while (src_char[i] >= '0' && src_char[i] < '8') {
                calc *= 8;
                calc += src_char[i] - '0';
                ++i;
            }
            //if (src_char[i] >= '0' && src_char[i] <= '9') { calc += src_char[i] - '0';} else {--i;};
            --i;
            des_char[j++] = calc;
        }
    }
    des_char[j] = '\0';//qDebug("GDBMIStackClient::gdbstrToUnicode");
#ifdef Q_OS_WIN
    //QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GBK"));
    return QString::fromLocal8Bit(des_char,j);
#else
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#endif
    return QString::fromLocal8Bit(des_char,j);
}

GDBMIStackClient::Frame GDBMIStackClient::readFrame (MIResult * result)
{
    MIValue *value;
    Frame frame;
    frame.m_line    = 0;
    frame.m_level    = -1;

    if ((value = GetMIValue (result, "func")) != 0)
        frame.m_func = value->m_string;
    if ((value = GetMIValue (result, "fullname")) != 0)
        frame.m_file = gdbstrToUnicode(value->m_string);
    if ((value = GetMIValue (result, "line")) != 0)
        frame.m_line = atoi (value->m_string);
    if ((value = GetMIValue (result, "level")) != 0)
        frame.m_level = atoi (value->m_string);

    return frame;
}

void GDBMIStackClient::readFrameArgs (MIResult * result, Frame * frame)
{
    MIValue * value = GetMIValue (result, "args");
    if (value && value->m_pList->m_type == VALUE)
    {
        value = value->m_pList->m_pValue;

        while (value)
        {
            MIValue * nameValue = GetMIValue (value->m_pResult, "name");
            MIValue * valueValue = GetMIValue (value->m_pResult, "value");

            if (nameValue && valueValue)
                frame->m_args.push_back (ARGPAIR (
                    nameValue->m_string, valueValue->m_string));

            value = value->m_pNext;
        }
    }
}

QString    GDBMIStackClient::formatFrame (Frame * frame)
{
    QString args;
    foreach (const ARGPAIR & arg, frame->m_args)
    {
        if (! args.isEmpty ())
            args += ",";
        args += QString ("%1=%2").arg (arg.first, arg.second);
    }

    return QString ("%1(%2), %3, line %4").arg (
        frame->m_func, args, frame->m_file).arg (frame->m_line);
}

void GDBMIStackClient::onResultParseErr(int /*token*/)
{
    return;
}

bool GDBMIStackClient::onResultRecord (MIResultRecord * resultRecord)
{
    if (IS_RESULT_RECORD (resultRecord, "stack"))
    {
        //qDebug ("stack result echo");
        MIResult * result = resultRecord->m_pResult->m_pValue->m_pList->m_pResult;

        while (result && result->m_pValue)
        {
            m_frames.push_back (readFrame (result->m_pValue->m_pResult));

            result = result->m_pNext;
        }

        if (m_refresh && ! m_frames.empty ()) {
            std::list<Frame>::iterator itr = m_frames.begin();
            if (stack_depth == -1) {
                stack_fullfilename = itr->m_file;
                stack_funcname = itr->m_func;
                stack_line = itr->m_line;
                stack_depth = m_currentStackDepth;
                //emit stackListChanged(false);
                isStackChanged = false;
                //qDebug(stack_funcname.toLatin1().constData());
            } else {
                //qDebug(stack_fullfilename.toLatin1().constData());
                //qDebug(itr->m_file.toLatin1().constData());

                if (QString::compare(stack_fullfilename,itr->m_file) != 0 && itr->m_file.size()) {
                    //if the cur_file changed,it indicates that the cur-filetab should change to another source file.
                    s_mainWindow->onTargetStop(itr->m_file,itr->m_line);
                }
                if (QString::compare(stack_fullfilename,itr->m_file) ||
                QString::compare(stack_funcname,itr->m_func) ||
                stack_depth != m_currentStackDepth) {
                    isStackChanged = true;
                    //emit stackListChanged(true);//qDebug("stack changed!");
                } else {
                    isStackChanged = false;
                    //emit stackListChanged(false);//qDebug("stack not changed!");
                }

                stack_fullfilename = itr->m_file;
                stack_funcname = itr->m_func;
                stack_line = itr->m_line;
                stack_depth = m_currentStackDepth;
                //qDebug(stack_funcname.toLatin1().constData());
            }
        }

        m_refresh = false;
    }
    else if (IS_RESULT_RECORD (resultRecord, "stack-args"))
    {
        MIResult * result = resultRecord->m_pResult->m_pValue->m_pList->m_pResult;

        std::list<Frame>::iterator it = m_frames.begin ();
        while (result && result->m_pValue && it != m_frames.end ())
        {
            readFrameArgs (result->m_pValue->m_pResult, & (* it));

            QListWidgetItem * item = new QListWidgetItem (
                formatFrame (& (* it)), m_stackList);
            item->setData (Qt::UserRole, QVariant (it->m_line));

            result = result->m_pNext;
            ++ it;
        }

        m_frames.clear ();
    }
    else if (IS_RESULT_RECORD (resultRecord, "depth"))
    {
        m_currentStackDepth = atoi (GetMIValue (resultRecord->m_pResult, "depth")->m_string);
        if (m_currentStackDepth > 0)
        {
            m_refresh = true;
            m_frames.clear ();
            m_stackList->clear ();

            for (int depth = 0; depth < m_currentStackDepth; depth += 1000)
            {
                sendRequest (QString ("-stack-list-frames %1 %2").arg (depth).arg (depth + (1000 - 1)));
                sendRequest (QString ("-stack-list-arguments 1 %1 %2").arg (depth).arg (depth + (1000 - 1)));
            }
        }
    }

    return false;
}

void GDBMIStackClient::onStopped (STOPREASON)
{
    if (m_stackList)
    {
        sendRequest ("-stack-info-depth");
    }
}

void GDBMIStackClient::onExit ()
{
    if (m_stackList)
        m_stackList->clear ();

    m_currentStackDepth = 0;
    m_frames.clear ();

    stack_depth = -1;
}

/*
bool GDBMIStackClient::isInMain()
{
    if (stack_funcname.length() == 0) {
        return true;
    }
    qDebug("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
    qDebug(stack_funcname.toLatin1().constData());
    if (stack_funcname.compare("main")) {
        return false;
    } else {
        return true;
    }
}*/


GDBMIVariableClient::GDBMIVariableClient (
    GDBMI *pThread, QTreeWidget *localTree, QTreeWidget *watchTree)
        : GDBMIClient (pThread), m_localTree (localTree),
        m_watchTree (watchTree)
{
    QStringList header;
    header << QObject::tr("Name") << QObject::tr("Type") << QObject::tr("Value");

    m_localTree->setHeaderLabels (header);
    m_watchTree->setHeaderLabels (header);

    m_root = new MIVariable ("", true);
    m_root->setStatus (MIV_ROOT);

    m_manager = new VarTreeManager (this);

    QObject::connect (m_localTree, SIGNAL (itemExpanded (QTreeWidgetItem * )),
        m_manager, SLOT (onItemExpanded (QTreeWidgetItem *)));
    QObject::connect (m_localTree, SIGNAL (itemDoubleClicked (QTreeWidgetItem *,int)),
        m_manager, SLOT (onItemDoubleClicked (QTreeWidgetItem *,int)));
    QObject::connect (m_watchTree, SIGNAL (itemExpanded (QTreeWidgetItem * )),
        m_manager, SLOT (onItemExpanded (QTreeWidgetItem *)));
    QObject::connect (m_watchTree, SIGNAL (itemDoubleClicked (QTreeWidgetItem *,int)),
        m_manager, SLOT (onItemDoubleClicked (QTreeWidgetItem *,int)));

    m_temp = 0;
    time_ticker.start();
}

void GDBMIVariableClient::myRetranslateUi()
{
    QStringList header;
    header << QObject::tr("Name") << QObject::tr("Type") << QObject::tr("Value");

    m_localTree->setHeaderLabels (header);
    m_watchTree->setHeaderLabels (header);
}

GDBMIVariableClient::~GDBMIVariableClient ()
{
    delete m_root;
    delete m_manager;
}

void GDBMIVariableClient::clearWatchTreeContent ()
{
    int cnt = m_watchTree->topLevelItemCount ();
    for (int i = 0; i < cnt; i++)
    {
        QTreeWidgetItem * item = m_watchTree->topLevelItem (i);
        item->setText (1, "");
        item->setText (2, "");

        REMOVE_ITEM_CHILD (item);
        item->setExpanded (false);
    }
}

void getExpandedItems(QStringList &items,QList<int>& levels,QTreeWidgetItem* treeItem,int level)
{
    items.append(treeItem->text(0));
    levels.append(level);
    qDebug("%s\t%d",treeItem->text(0).toUtf8().constData(),level);
    for (int i = 0;i < treeItem->childCount();++i) {
        QTreeWidgetItem * item = treeItem->child(i);
        if (item->isExpanded())
            getExpandedItems(items,levels,item,level+1);
    }
    return;
}

void setExpandedItems(QStringList items,QList<int>& levels,QTreeWidgetItem* treeItem,int &pos,int level)
{

    for (int i = 0;i < treeItem->childCount();++i) {
        if (pos+1 >= items.count()) return;
        QTreeWidgetItem * item = treeItem->child(i);
        if (!items.at(pos+1).compare(item->text(0))) {
            if (levels.at(pos+1) == level+1) {
                item->setExpanded(true);
                ++pos;
                setExpandedItems(items,levels,item,pos,level+1);
            }
        }

    }
}

void GDBMIVariableClient::updateLocalTree ()
{
    TRACEFUNC (updateLocalTree);

    QStringList expandedItems;
    QList<int>    itemLevel;

    for (int i = 0;i < m_localTree->topLevelItemCount();++i) {
        if (m_localTree->topLevelItem(i)->isExpanded()) {
            getExpandedItems(expandedItems,itemLevel,m_localTree->topLevelItem(i),0);
            //expandedItems.append(m_localTree->topLevelItem(i)->text(0));
            //qDebug(m_localTree->topLevelItem(i)->text(0).toLatin1().constData());
        }
    }

    m_localTree->clear ();
    for (MIVariable::VARLIST::iterator it = m_root->m_children.begin ();
        it != m_root->m_children.end ();
        ++ it)
    {
        QTreeWidgetItem * item = new QTreeWidgetItem (m_localTree);
        updateTreeItem (item, * it, true);

        m_localTree->addTopLevelItem (item);
        int pos = expandedItems.indexOf(item->text(0));
        if (pos != -1) {
            if ( itemLevel.at(pos) == 0) {
                item->setExpanded(true);
                setExpandedItems(expandedItems,itemLevel,item,pos,0);
            }
        }
    }
}

void GDBMIVariableClient::updateTreeItem (
    QTreeWidgetItem * item, MIVariable * var, bool child)
{
    item->setData (0, Qt::UserRole, QVariant::fromValue (var));
    item->setText (1, var ? var->m_type : "");
    item->setText (2, var ? var->m_value : "");

    if (var && item->text (0).isEmpty ())
        item->setText (0, var->m_name.section ('.', -1));

    if (var && (var->status () & MIV_MORE))
    {
        ////qDebug ("add MORE item");
        item->setExpanded(false);
        REMOVE_ITEM_CHILD(item);
        QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
        childItem->setText (0, "reading contents...");
        childItem->setFirstColumnSpanned (true);
    }
    else if (child)
    {
        //qDebug("add child item.");
        REMOVE_ITEM_CHILD (item);
        if (var && var->m_children.empty ())
        {
            REMOVE_ITEM_CHILD (item);
        }
        else if (var)
        {
            int nc = item->childCount ();
            bool add = (nc == 0 ? true : false);

            Q_ASSERT_X (! (nc && static_cast<unsigned int> (nc) != var->m_children.size ()), "updateTreeItem",
                "incorrect var object children size");

            int i = 0;
            for (MIVariable::VARLIST::iterator it = var->m_children.begin ();
                it != var->m_children.end ();
                ++ it, ++ i)
            {
                QTreeWidgetItem * childItem;

                if (add)
                    childItem = new QTreeWidgetItem (item);
                else
                    childItem = item->child (i);

                updateTreeItem (childItem, * it, true);
            }
        }
    }
}

void GDBMIVariableClient::updateWatchTree ()
{
    TRACEFUNC (updateWatchTree);

    if (! m_watchTree)
        return;

    int cnt = m_watchTree->topLevelItemCount ();
    for (int i = 0; i < cnt; i++)
        updateWatchItem (m_watchTree->topLevelItem (i));
}

void GDBMIVariableClient::updateWatchItem (QTreeWidgetItem * item)
{
    QString name = item->text (0);
    MIVariable * var = m_root->findVariable (name);

    if (! var)
    {
        WatchItem * watch = findWatchItemByExpr (name);

        if (watch)
            var = watch->m_var;
        else
        {
            // We have NOT tried to create a variable object
            // yet. Now it is time to do that
            m_watches.push_back (WatchItem (name));

            int token = sendRequest (QString ("-var-create GUIDE__%1 * %2").arg(name).arg (name));
            //int token = sendRequest (QString ("-var-create - * %1").arg(name));//.arg (name));
            if (token) {
                m_requestList.push_back (RequestPair (name, token));
                //qDebug("%d\t%s\n",token,name.toLatin1().constData());
            }
        }
    }

    updateTreeItem (item, var, true);
}

void GDBMIVariableClient::updateWatchItem (WatchItem * w)
{
    Q_ASSERT (w);
    // a watch item always reference to a top item
    // in QTreeWidget

    QTreeWidgetItem * item;
    if ((item = findTreeItem (m_watchTree, w->m_expr, true)) != 0)
        updateTreeItem (item, w->m_var, true);
}

void GDBMIVariableClient::updateVariableObject (const QString & name, bool create)
{
    TRACEFUNC(updateVariableObject);
    if (create)
        sendRequest (QString ("-var-create %1 * %1").arg (name));
    else
    {
        int token = sendRequest (QString (
            "-var-list-children --simple-values %1").arg (name));
        if (token)
            m_requestList.push_back (RequestPair (name, token));
    }
}

void GDBMIVariableClient::updateVariableObject (const QString & name, bool create,int start,int array_length)
{
    //TRACEFUNC(updateVariableObject);
    if (create) {
        int token = sendRequest (QString ("-var-create %1 * %1").arg (name));
        if (token)
            m_requestList.push_back (RequestPair (name, token));
    }
    /*
    else if (array_length <= 3200) {
        int token = sendRequest (QString (
            "-var-list-children --simple-values %1").arg (name));
        if (token)
            m_requestList.push_back (RequestPair (name, token));

        for (std::list<struct ArrayStart>::iterator it = m_arrayStartNum.begin();it != m_arrayStartNum.end();++it) {
            if (it->m_name.compare(name) == 0) {
                m_arrayStartNum.erase(it);break;
            }
        }
    }*/
    else
    {
        int start_num,end_num;
        int stop_num;
        stop_num = (start/1000+start%1000 >= array_length)?array_length:start/1000+start%1000;

        for (std::list<struct ArrayStart>::iterator it = m_arrayStartNum.begin();it != m_arrayStartNum.end();++it) {
            if (it->m_name.compare(name) == 0) {
                end_num = (it->m_start/1000+start%1000>it->m_arrayLength)?it->m_arrayLength:it->m_start/1000+start%1000;
                start_num = it->m_start/1000;
                /*
                if (it->m_start <= start) {
                    for (int i = it->m_start;i < start;++i) {
                        deleteVariableObject(QString("%1.%2").arg(name).arg(i));
                    }
                } else {
                    for (int i = stop_num;i < end_num;++i) {
                        deleteVariableObject(QString("%1.%2").arg(name).arg(i));
                    }
                }*/
                for (int i = it->m_start/1000;i < end_num;++i) {
                    deleteVariableObject(QString("%1.%2").arg(name).arg(i));
                }
                MIVariable *var = findVariable(it->m_name);
                for (MIVariable::VARLIST::iterator tt = var->m_children.begin ();tt != var->m_children.end ();tt++) {
                    delete (*tt);
                }
                var->m_children.clear();
                m_arrayStartNum.erase(it);break;
            }
        }
        QStringList nameList = name.split('.');
        QString var_name = nameList[0];
        QRegExp rx("(\\d+)");

        for (int i = 1;i < nameList.size();++i) {
            if (rx.indexIn(nameList[i],0) != -1) {
                var_name = QString("%1[%2]").arg(var_name).arg(nameList[i]);
            } else {
                var_name = QString("%1.%2").arg(var_name).arg(nameList[i]);
            }
        }
        if (var_name.contains("GUIDE__")) {
            var_name = var_name.right(var_name.length() - 7);
        }
        for (int i = start/1000;i < stop_num;++i) {
            int token = sendRequest (QString (
                        "-var-create %1.%2 * %3[%4]").arg (name).arg(i).arg(var_name).arg(i));
            if (token)
                    m_requestList.push_back (RequestPair (name, token));
        }
        /*
        if (start_num <= start) {
            for (int i = end_num;i < stop_num;++i) {
                int token = sendRequest (QString (
                        "-var-create %1.%2 * %3[%4]").arg (name).arg(i).arg(var_name).arg(i));
                if (token)
                        m_requestList.push_back (RequestPair (name, token));
            }
        } else {
        }*/
    }


    struct ArrayStart array_start;
    array_start.m_name = name;array_start.m_start = start;array_start.m_arrayLength = array_length;
    m_arrayStartNum.push_front(array_start);
}

void GDBMIVariableClient::deleteVariableObject (const QString & name)
{
    sendRequest (QString ("-var-delete %1").arg (name));
    //qDebug(QString ("-var-delete %1").arg (name).toLatin1().constData());
}

void GDBMIVariableClient::updateVariable (MIValue * value)
{
    TRACEFUNC (updateVariable);

    MIValue * v;
    MIVariable * var = 0;
    bool add = false;

    if ((v = GetMIValue (value->m_pResult, QString("name").toLatin1().data())) != 0)
    {
        var = m_root->findVariable (v->m_string);
        if (! var)
        {
            var = new MIVariable (v->m_string);
            add = true;
        }
        else
        {
            // reset its status to NORMAL to prevent
            // it from being deleted later
            var->clearStatus (MIV_MARK);
        }

        readVariable (value->m_pResult, var);

        //if (add && var->m_value.isEmpty ())
        if (add && var->m_isComplex)
        {
            // this variable is of compound type, whose value
            // can NOT be determined right now
            var->setStatus (MIV_MORE);
        }

        if (add)
        {
            m_root->addChild (var);
            /*qDebug ("add var (%s, %s, %s)",
                var->m_name.toLatin1 ().constData (),
                var->m_type.toLatin1 ().constData (),
                var->m_value.toLatin1 ().constData ());*/
        }
    }
}

int GDBMIVariableClient::readVariable (MIResult * result, MIVariable * var)
{
    MIValue * v;

    if ((v = GetMIValue (result, QString("type").toLatin1().data())) != 0)
        var->m_type = v->m_string;
    if ((v = GetMIValue (result, QString("value").toLatin1().data())) != 0) {
        var->m_value = ConvertCString(v->m_string);
    } else {
        var->m_isComplex = true;
    }

    if ((v = GetMIValue (result, QString("numchild").toLatin1().data())) != 0) {
        var->m_childnum = atoi(v->m_string);
    } else {
        var->m_childnum = 0;
    }



    if (v = GetMIValue (result, QString("numchild").toLatin1().data())) {
       int n = atoi (v->m_string);

       if (n) {
           QRegExp rx("0x*");
           rx.setPatternSyntax(QRegExp::Wildcard);
           if (!rx.exactMatch(var->m_value)) {
               var->m_value.clear();
           }
           //var->m_value.clear();
           var->m_isComplex = true;
       }
       else if (var->m_value.isEmpty ())
       {
           // here the numchild indicate that this is a simple variable
        // but we have no value right now, try to get it
           var->m_value = " ";

        VRequestPair vrp;
        vrp.first = sendRequest (QString ("-var-evaluate-expression %1").arg (var->m_name));
        vrp.second = var;

        m_vRequestList.push_back (vrp);
       }

       return n;
    }
    else
        return 0;
}

bool GDBMIVariableClient::addWatch (const QString & expression)
{
    QString expr = expression;
    expr.remove (QRegExp ("[ \\r\\n\\t]"));

    if (indexAtWatchTable (expr) != -1)
        return false;

    QTreeWidgetItem * item = new QTreeWidgetItem (m_watchTree);
    item->setText (0, expr);

    m_watchTree->addTopLevelItem (item);

    if (m_pGDBMI->isStarted ())
        updateWatchItem (item);

    return true;
}

void GDBMIVariableClient::delWatch (int idx)
{
    if (idx != -1)
    {
        QTreeWidgetItem * item = m_watchTree->takeTopLevelItem (idx);
        QString expr = item->text (0);

        for (std::list<WatchItem>::iterator it = m_watches.begin ();
            it != m_watches.end ();
            ++ it)
        {
            if (it->m_expr == expr)
            {
                if (it->m_var && (it->m_var->status () & MIV_VAROBJ))
                    deleteVariableObject (it->m_var->m_name);
                if (it->m_var) {
                    delete it->m_var;
                    it->m_var=0;
                }
                m_watches.erase (it);
                break;
            }
        }

        delete item;
    }
}

int GDBMIVariableClient::indexAtWatchTable (const QString & name)
{
    if (m_watchTree)
    {
        int cnt = m_watchTree->topLevelItemCount ();
        for (int i = 0; i < cnt; i++)
        {
            if (m_watchTree->topLevelItem (i)->text (0) == name)
                return i;
        }
    }

    return -1;
}

void GDBMIVariableClient::onStopped (STOPREASON)
{
    sendRequest ("-var-update --simple-values *");
    sendRequest ("-stack-list-locals --simple-values");
    //sendRequest ("-stack-list-arguments 1");
    //*/
}

void GDBMIVariableClient::onExit ()
{
    for (std::list<WatchItem>::iterator it = m_watches.begin ();
        it != m_watches.end ();
        ++ it)
        delete it->m_var;
    m_watches.clear ();

    m_root->removeAllChild ();
    m_requestList.clear();

    m_vRequestList.clear ();

    m_localTree->clear ();
    clearWatchTreeContent ();

    // here maybe we have some (or many) var objects
    // created, just discard them since gdb will exit
    // soon
}

void GDBMIVariableClient::parseChildrenList (MIVariable * var, MIResult * result)
{
    TRACEFUNC (parseChildrenList);
    //qDebug();


    while (result)
    {
        MIValue * v = GetMIValue (result->m_pValue->m_pResult, QString("name").toLatin1().data());
         if (v)
         {
             ////qDebug ("find one child");
            // gdb automatically creates var object for this one
             MIVariable * child = new MIVariable (v->m_string, MIV_VAROBJ);

            readVariable (result->m_pValue->m_pResult, child);
            var->addChild (child);
 
             //if (child->m_value.isEmpty ())
            if (child->m_isComplex)
             {
                child->setStatus (MIV_MORE);
             }
         }
        else
        {
            //qDebug ("oops, no name found");
        }

        result = result->m_pNext;
    }
}

void GDBMIVariableClient::parseChildrenList (MIVariable * var, MIResult * result,int start,int end)
{
    TRACEFUNC (parseChildrenList2);
    //qDebug(QString("--%1--%2").arg(start).arg(end).toLatin1().constData());
    int i = 0;
    while (result)
    {
        MIValue * v = GetMIValue (result->m_pValue->m_pResult, QString("name").toLatin1().data());
         if (v)
         {
            if (i++ < start && start!= -1 && end != -1) {
                result = result->m_pNext;
                continue;
            }
            else if (i > end && start!= -1 && end != -1) {
                return;
            }
            ////qDebug ("find one child");
            // gdb automatically creates var object for this one
             MIVariable * child = new MIVariable (v->m_string, MIV_VAROBJ);

            readVariable (result->m_pValue->m_pResult, child);
            var->addChild (child);
 
             //if (child->m_value.isEmpty ())
             if (child->m_isComplex)
            {
                child->setStatus (MIV_MORE);
             }
         }
        else
        {
            //qDebug ("oops, no name found");
        }

        result = result->m_pNext;
    }
}

QTreeWidgetItem * GDBMIVariableClient::findTreeItem (
    QTreeWidget * widget, const QString & name, bool onlyTop)
{
    //qDebug("1302");
    if (name.isEmpty ())
        return false;

    QStringList        nameList;
    QTreeWidgetItem    * item = 0;

    if (! onlyTop) {
        if (name.contains("GUIDE__")) {
            nameList = name.right(name.length() - 7).split('.');
        } else {
            nameList = name.split ('.');
        }
    }
    else
        nameList.push_back (name);

    int cnt = widget->topLevelItemCount ();
    for (int i = 0; i < cnt; i++)
    {
        item = widget->topLevelItem (i);

        QString tmp_name = "";
        bool flag = false;
        //qDebug("******************");
        //qDebug(item->text(0).toLatin1().constData());
        int ii = 0;
        for (ii = 0;ii < nameList.count();++ii) {
            tmp_name += nameList[ii];
            //qDebug(tmp_name.toLatin1().constData());
            if (item->text(0) == tmp_name) {
                flag = true;break;
            }
            tmp_name += ".";
        }


        //if (item->text (0) == nameList.front ())
        if (flag)
        {
            //qDebug("%d\n",ii);
            for (;ii >= 0;--ii) {
                nameList.pop_front ();
            }
            if (nameList.count() == 0)
                return item;

            if (! onlyTop)
            {
                //nameList.pop_front ();

                foreach (const QString & pattern, nameList)
                {
                    QTreeWidgetItem * result = 0;

                    int n = item->childCount ();
                    for (int c = 0; c < n; c++)
                    {
                        QTreeWidgetItem * child = item->child (c);
                        if (pattern == child->text (0))
                        {
                            result = child;
                            break;
                        }
                    }

                    if (! result)
                        return 0;
                    item = result;
                }
            }

            break;
        }
        else
            item = 0;
    }

    return item;
}

bool GDBMIVariableClient::findTreeItem (
        QTreeWidgetItem * item, const QString & name, ItemSet & set)
{
    bool    ret = false;
    QString    text = item->text (0);

    if (text.startsWith (name))
    {
        int pos = name.length ();
        if (pos == text.length ())
            set.push_back (item);
        else
        {
            text = text.mid (pos);

            int cnt = item->childCount ();
            for (int i = 0; i < cnt; i++)
            {
                if (findTreeItem (item->child (i), text, set))
                    ret = true;
            }
        }
    }

    return ret;
}

void GDBMIVariableClient::updateDelayedItem (MIVariable * var)
{
    TRACEFUNC (updateDelayedItem);

    // This function will be called when the echo
    // of "-var-list-children" received. Here each variable
    // object is a leaf node, which does NOT contain
    // any child
    //qDebug (var->m_name.toLatin1 ().constData ());

    QTreeWidgetItem * item;
    if ((item = findTreeItem (m_localTree, var->m_name)) != 0)
    {
        //qDebug ("delayed update local tree 01");
        // delete "waiting..." node if exist
        if (item->childCount () == 1)
            delete item->takeChild (0);

        updateTreeItem (item, var, true);
        item->setExpanded(true);
    }

    int index = var->m_name.indexOf('.');
    WatchItem * wi = findWatchItemByName (var->m_name.left(index));
    QString item_name = var->m_name;
    if (wi) {
        if (index != -1)
            item_name.replace(0,index,wi->m_expr);
        else
            item_name = wi->m_expr;
    }
    if (wi && (item = findTreeItem (m_watchTree, item_name)))
    {
        //qDebug ("delayed update watch tree 01");
        // delete "waiting..." node if exist
        if (item->childCount () == 1)
            delete item->takeChild (0);
        updateTreeItem (item, var, true);
        item->setExpanded(true);
    }

    if ((item = findTreeItem (m_localTree, item_name)) != 0)
    {
        //qDebug ("delayed update local tree 02");
        // delete "waiting..." node if exist
        if (item->childCount () == 1)
            delete item->takeChild (0);

        updateTreeItem (item, var, true);
        item->setExpanded(true);
    }

    if (! wi && (item = findTreeItem (m_watchTree, var->m_name)))
    {
        //qDebug ("delayed update watch tree 02");
        // delete "waiting..." node if exist
        if (item->childCount () == 1)
            delete item->takeChild (0);
        updateTreeItem (item, var, true);
        item->setExpanded(true);
    }
    /*
    if (! wi && (item = findTreeItem (m_watchTree, var->m_name,true)))
    {
        //qDebug ("delayed update watch tree");
        // delete "waiting..." node if exist
        if (item->childCount () == 1)
            delete item->takeChild (0);
        updateTreeItem (item, var, true);
        item->setExpanded(true);
    }*/
}

void GDBMIVariableClient::markAll ()
{
    for (MIVariable::VARLIST::iterator it = m_root->m_children.begin ();
        it != m_root->m_children.end ();
        ++ it)
    {
        (* it)->setStatus (MIV_MARK);
    }
}

void GDBMIVariableClient::truncMarked ()
{
    MIVariable::VARLIST::iterator it = m_root->m_children.begin (), temp;
    while (it != m_root->m_children.end ())
    {
        if ((* it)->status () & MIV_MARK)
        {
            temp = it ++;

            if ((* temp)->status () & MIV_VAROBJ)
                deleteVariableObject ((* temp)->m_name);
            delete * temp;

            m_root->m_children.erase (temp);
        }
        else
            ++ it;
    }
}

void GDBMIVariableClient::setIsPascal(bool isPascal)
{
    m_IsPascal = isPascal;
}

void GDBMIVariableClient::onResultParseErr(int token)
{
    //qDebug("ERR %d\n",token);
    RequestList::iterator it = findRequest (token);
    QTreeWidgetItem * item;
    //local var
    MIVariable * var;
    if (m_IsPascal) {
        var = m_root->findVariable (it->first,true);
    } else {
        var = m_root->findVariable (it->first);
    }

    // the var object created by watch
    WatchItem * watch;
    if ((watch = findWatchItemByName (it->first)) != 0 &&
        (item = findTreeItem (m_watchTree, watch->m_expr, true)) != 0) {
        REMOVE_ITEM_CHILD(item);
        QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
        childItem->setText (0, "Array is too large to expand.");
        childItem->setFirstColumnSpanned (true);
    }

    if (var) {

        // the var object contained in local tree
        if ((item = findTreeItem (m_localTree, var->m_name)) != 0) {
            REMOVE_ITEM_CHILD(item);
            QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
            childItem->setText (0, "Array is too large to expand.");
            childItem->setFirstColumnSpanned (true);
        }

        // the var object contained in local tree maybe
        // referenced by watch
        if ((item = findTreeItem (m_watchTree, var->m_name)) != 0) {
            REMOVE_ITEM_CHILD(item);
            QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
            childItem->setText (0, "Array is too large to expand.");
            childItem->setFirstColumnSpanned (true);
        }
        /*
        */
        return;
    }

    WatchItem *wItem = findWatchItemByName (it->first);
    if (wItem) {
        //qDebug("wItem%s\n",wItem->m_expr.toLatin1().constData());
        if (m_IsPascal) {
            var = m_root->findVariable (wItem->m_expr,true);
        } else {
            var = m_root->findVariable (wItem->m_expr);
        }

        // the var object created by watch
        WatchItem * watch;
        if ((watch = findWatchItemByName (wItem->m_expr)) != 0 &&
            (item = findTreeItem (m_watchTree, watch->m_expr, true)) != 0) {
            REMOVE_ITEM_CHILD(item);
            QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
            childItem->setText (0, "Array is too large to expand.");
            childItem->setFirstColumnSpanned (true);
        }
    }

    if (var) {

        // the var object contained in local tree
        if ((item = findTreeItem (m_localTree, var->m_name)) != 0) {
            REMOVE_ITEM_CHILD(item);
            QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
            childItem->setText (0, "Array is too large to expand.");
            childItem->setFirstColumnSpanned (true);
        }

        // the var object contained in local tree maybe
        // referenced by watch
        if ((item = findTreeItem (m_watchTree, var->m_name)) != 0) {
            REMOVE_ITEM_CHILD(item);
            QTreeWidgetItem * childItem = new QTreeWidgetItem (item);
            childItem->setText (0, "Array is too large to expand.");
            childItem->setFirstColumnSpanned (true);
        }
    }
    return;
}

bool GDBMIVariableClient::onResultRecord (MIResultRecord * resultRecord)
{
    //TRACEFUNC(onResultRecord);
    //qDebug(QString("Time_ticker ->%1").arg(time_ticker.elapsed()).toLatin1().constData());
    if (IS_RESULT_ERROR (resultRecord))
    {
        // ignore
        //qDebug ("ignore result record");
    }
    else if (IS_RESULT_RECORD (resultRecord, "value"))
    {
        // echo of -var-evaluate-expression
        MIVariable * var = NULL;

        for (VRequestList::iterator it = m_vRequestList.begin (); it != m_vRequestList.end (); ++it)
        {
            if (it->first == resultRecord->m_token)
            {
                var = it->second;
                m_vRequestList.erase (it);
                break;
            }
        }

        if (var)
        {
            var->m_value = GetMIValue (resultRecord->m_pResult, QString("value").toLatin1().data())->m_string;
            QTreeWidgetItem * item;

            // the var object contained in local tree
            if ((item = findTreeItem (m_localTree, var->m_name)) != 0)
                updateTreeItem (item, var, false);
            // the var object contained in local tree maybe
            // referenced by watch
            if ((item = findTreeItem (m_watchTree, var->m_name)) != 0)
                updateTreeItem (item, var, false);
            // the var object created by watch
            WatchItem * watch;
            if ((watch = findWatchItemByName (var->m_name)) != 0 &&
                (item = findTreeItem (m_watchTree, watch->m_expr, true)) != 0) {
                updateTreeItem (item, var, false);
            }
        }
    }
    else if (IS_RESULT_RECORD (resultRecord, "locals"))
    {
        if (isStackChanged) {
            //qDebug("Delete Var Object");
            for (MIVariable::VARLIST::iterator it = m_root->m_children.begin ();
                it != m_root->m_children.end ();
                ++ it)
            {
                if ((*it)->status() & MIV_VAROBJ) {
                    deleteVariableObject((*it)->m_name);
                    //qDebug((*it)->m_name.toLatin1().constData());
                }
            }
            m_root->removeAllChild();

            for (std::list<WatchItem>::iterator it = m_watches.begin();it != m_watches.end();++it) {
                if (it->m_var && (it->m_var->status() & MIV_VAROBJ)) {
                    deleteVariableObject(it->m_name);
                    //qDebug(it->m_name.toLatin1().constData());
                }
                delete it->m_var;
            }
            m_watches.clear();
        }

        markAll ();

        MIList * list = resultRecord->m_pResult->m_pValue->m_pList;
        if (list->m_type == VALUE)
        {
            MIValue * value = list->m_pValue;
            while (value)
            {
                updateVariable (value);
                value = value->m_pNext;
            }
        }

        truncMarked ();

        updateLocalTree ();
        updateWatchTree ();
    }
    /*else if (IS_RESULT_RECORD(resultRecord,"stack-args")) {

        MIList * list = resultRecord->m_pResult->m_pValue->m_pList;
        MIResult * result = list->m_pResult->m_pValue->m_pResult;
        while (strcmp(result->m_variable,"args")) {
            result = result->m_pNext;
        }
        if (result->m_pValue->m_pList->m_type==EMPTY)
            return false;
        MIValue * value = result->m_pValue->m_pList->m_pValue;

        while (value) {
            updateVariable(value);
            value = value->m_pNext;
        }
        updateLocalTree ();
        updateWatchTree ();
    }*/
    else if (IS_RESULT_RECORD (resultRecord, "changelist"))
    {
        MIList * list = resultRecord->m_pResult->m_pValue->m_pList;
        if (list->m_type == VALUE)
        {
            MIValue * value = list->m_pValue;
            while (value)
            {
                MIValue * v;
                MIVariable * var;

                if ((v = GetMIValue (value->m_pResult, QString("name").toLatin1().data())) != 0 &&
                    (var = findVariable (v->m_string)) != 0)
                {
                    if ((v = GetMIValue (value->m_pResult, QString("value").toLatin1().data())) != 0)
                        var->m_value = v->m_string;

                    QTreeWidgetItem * item;

                    // the var object contained in local tree
                    if ((item = findTreeItem (m_localTree, var->m_name)) != 0)
                        updateTreeItem (item, var, false);

                    // the var object contained in local tree maybe
                    // referenced by watch
                    if ((item = findTreeItem (m_watchTree, var->m_name)) != 0)
                        updateTreeItem (item, var, false);

                    // the var object created by watch
                    WatchItem * watch;
                    if ((watch = findWatchItemByName (var->m_name)) != 0 &&
                        (item = findTreeItem (m_watchTree, watch->m_expr, true)) != 0) {
                        updateTreeItem (item, var, false);
                    }
                }

                value = value->m_pNext;
            }
        }
    }
    else if (IS_RESULT_RECORD (resultRecord, "name"))
    {
        // echo of "-var-create"
        //qDebug ("echo of -var-create\n");

        MIValue * value = GetMIValue (resultRecord->m_pResult, QString("name").toLatin1().data());

        if (value && value->m_type == VT_CONST)
        {
            MIVariable * v;
            if (m_IsPascal) {
                v = m_root->findVariable (value->m_string,true);
            } else {
                v = m_root->findVariable (value->m_string);
            }

            if (v)
            {
                //qDebug ("add %s varobj", value->m_string);
                //qDebug("find in locals");
                v->setStatus (MIV_VAROBJ);
            }
            else
            {
                // not found in local table
                //qDebug("not find in locals");

                RequestList::iterator it = findRequest (resultRecord->m_token);
                //qDebug("token %d\t %s\n",resultRecord->m_token,it->first.toLatin1().constData());
                if (it != m_requestList.end ())
                {
                    WatchItem * item = findWatchItemByExpr (it->first);
                    //qDebug("it->first %s\n",it->first.toLatin1().constData());
                    if (item)
                    {
                        //qDebug("can we find here?");
                        item->m_name = value->m_string;
                        item->m_var = new MIVariable (value->m_string, MIV_VAROBJ);

                        readVariable (resultRecord->m_pResult, item->m_var);
                        //if (item->m_var->m_value.isEmpty ())
                        if (item->m_var->m_isComplex)
                            item->m_var->setStatus (MIV_MORE);

                        updateWatchItem (item);
                    } else {
                        //qDebug("can we find there?");
                        MIVariable * var = findVariable (it->first);
                        if (!var)  {
                            //qDebug("%s is not found!",it->first.toLatin1().constData());
                            return false;
                        }
                        //qDebug(it->first.toLatin1().constData());
                        MIVariable * child = new MIVariable ( value->m_string,MIV_VAROBJ );
                        readVariable(resultRecord->m_pResult,child);
                        //if (child->m_value.isEmpty()) {
                        if (child->m_isComplex) {
                            child->setStatus(MIV_MORE);
                        }

                        int temp = var->m_children.size();

                        int varNum = 0;
                        for (std::list<struct ArrayStart>::iterator iit = m_arrayStartNum.begin();iit != m_arrayStartNum.end();++iit) {
                            if (iit->m_name.compare(it->first) == 0) {
                                varNum = (iit->m_start/1000 + iit->m_start%1000 >= iit->m_arrayLength) ?
                                    iit->m_arrayLength:iit->m_start/1000 + iit->m_start%1000;
                                varNum -= iit->m_start/1000;
                                break;
                            }
                        }
                        if (temp >= varNum) {
                            for (MIVariable::VARLIST::iterator tt = var->m_children.begin ();tt != var->m_children.end ();tt++) {
                                delete (*tt);
                            }
                            var->m_children.clear();
                            temp = 0;
                        }
                        var->addChild(child);
                        temp++;
                        //qDebug(QString("---%1---%2").arg(temp).arg(varNum).toLatin1().constData());
                        if (temp >= varNum)
                            updateDelayedItem (var);
                        m_requestList.erase (it);
                    }
                }
                else
                {
                    //qDebug ("oops, unknown var object %s", value->m_string);

                }
            }

            QRegExp rx ("\\[(\\d+)\\]");
            ulong size = 0;
            if (v && rx.indexIn (v->m_type) != -1)
            {
                size = rx.cap (1).toULong ();
            }

            if (size > ARRAY_EAGE && v && (v->status () & MIV_EXPAND)) {
                QStringList nameList = v->m_name.split('.');
                QString var_name = nameList[0];
                QRegExp rxx("(\\d+)");

                for (int i = 1;i < nameList.size();++i) {
                    if (rxx.indexIn(nameList[i],0) != -1) {
                        var_name = QString("%1[%2]").arg(var_name).arg(nameList[i]);
                    } else {
                        var_name = QString("%1.%2").arg(var_name).arg(nameList[i]);
                    }
                }

                //qDebug(var_name.toLatin1().constData());
                int start_num = 0,end_num = 0;
                for (std::list<struct ArrayStart>::iterator it = m_arrayStartNum.begin();it != m_arrayStartNum.end();++it) {
                    if (it->m_name.compare(v->m_name) == 0) {
                        end_num = (it->m_start/1000 + it->m_start%1000 >= it->m_arrayLength) ? it->m_arrayLength:it->m_start/1000 + it->m_start%1000;
                        start_num = it->m_start/1000;
                        break;
                    }
                }
                //qDebug(QString("%1--%2").arg(start_num).arg(end_num).toLatin1().constData());


                for (int i = start_num;i < end_num;++i) {
                    deleteVariableObject(QString("%1.%2").arg(v->m_name).arg(i));
                    if (var_name.contains("GUIDE__")) {
                        var_name = var_name.right(var_name.length() - 7);
                    }
                    int token = sendRequest (QString (
                            "-var-create %1.%2 * %3[%4]").arg (v->m_name).arg(i).arg(var_name).arg(i));
                    if (token)
                        m_requestList.push_back (RequestPair (v->m_name, token));
                }
            }
            else if (v && (v->status () & MIV_EXPAND))
            {
                int token = sendRequest (QString (
                    "-var-list-children --simple-values %1").arg (v->m_name));
                if (token)
                    m_requestList.push_back (RequestPair (v->m_name, token));
                v->clearStatus (MIV_EXPAND);
            }
        }
    }
    else if (IS_RESULT_RECORD (resultRecord, "numchild"))
    {
        //qDebug ("echo of children list");
        for (std::list<RequestPair>::iterator it = m_requestList.begin ();
            it != m_requestList.end ();
            ++ it)
        {
            if (it->second == resultRecord->m_token)
            {
                MIValue * value = GetMIValue (resultRecord->m_pResult, QString("children").toLatin1().data());
                MIVariable * var = findVariable (it->first);

                /*if (var) {
                    qDebug("var useful");
                }
                if (value) {
                    qDebug("value useful");
                }*/

                if (var && value)
                {
                    //qDebug("var was found!");
                    int start_num,end_num;
                    start_num = end_num = -1;
                    for (std::list<struct ArrayStart>::iterator iit = m_arrayStartNum.begin();iit != m_arrayStartNum.end();++iit) {
                        if (iit->m_name.compare(it->first) == 0) {
                            start_num = iit->m_start/1000;
                            end_num = (iit->m_start/1000 + iit->m_start%1000 >= iit->m_arrayLength) ?
                                iit->m_arrayLength:iit->m_start/1000 + iit->m_start%1000;
                            break;
                        }
                    }
                    //qDebug(QString("%1:%2").arg(start_num).arg(end_num).toLatin1().constData());
                    if (start_num == -1 || end_num == -1) {
                        parseChildrenList (var, value->m_pList->m_pResult);
                    } else {
                        parseChildrenList (var, value->m_pList->m_pResult,start_num,end_num);
                    }
                    //qDebug(var->m_name.toLatin1().constData());
                    //qDebug(var->m_value.toLatin1().constData());
                    updateDelayedItem (var);
                } else {
                    //qDebug ("oops, no corresponding variable found");
                }

                m_requestList.erase (it);
                break;
            }
        }
    }
    else
    {
        // sth. else
        //qDebug ("oops, sth. unexpected");
    }

    return false;
}

void GDBMIMemoryClient::onStopped(STOPREASON)
{
    qDebug ("GDBMIMemoryClient onStopped");
    showMemory ();
}

void GDBMIMemoryClient::onExit()
{
    qDebug ("MemoryClient onExit");
    m_memBrowser->clear();
    //m_begin = QString("main");
}

bool GDBMIMemoryClient::onResultRecord (MIResultRecord * resultRecord)
{
    if (IS_RESULT_ERROR (resultRecord))
    {
        // ignore
        //qDebug ("ignore result record");
        MIValue * value = GetMIValue (resultRecord->m_pResult, QString("msg").toLatin1().data());
        QString errorMessage = value->m_string;
        if (errorMessage.startsWith("Unable to read memory"))
            errorMessage = QObject::tr("Unable to read memory");
        m_memBrowser->setText(errorMessage);
        return false;
    }
    if (m_pGDBMI->verIsNew()) {
        if (IS_RESULT_RECORD (resultRecord, "memory"))
        {
            // echo of -data-read-memory-bytes m_startAddr 40
            MIResult * result = resultRecord->m_pResult->m_pValue->m_pList->m_pValue->m_pResult;
            MIString var, value;
            while (result)
            {
                var = result->m_variable;
                value = result->m_pValue->m_string;
                if (!QString::compare(var, "begin")) m_begin = value;
                else if (!QString::compare(var, "offset")) m_offset = value;
                else if (!QString::compare(var, "end")) m_end = value;
                else if (!QString::compare(var, "contents")) m_contents = value;
                result = result->m_pNext;
            }
            QString memToShow = "";
            int begin, offset, end, first, endoffset, length;
            bool ok;
            begin = m_begin.toInt(&ok, 16);
            offset = m_offset.toInt(&ok, 16);
            end = m_end.toInt(&ok, 16);
            //sscanf(m_begin.toLatin1().constData(), "%x", &begin);
            //sscanf(m_offset.toLatin1().constData(), "%x", &offset);
            //sscanf(m_end.toLatin1().constData(), "%x", &end);
            first = begin - offset;
            length = end - begin;
            endoffset = m_showBytes - length;
            for (int i = 0; i < offset; i++) {
                m_contents = "??" + m_contents;
            }
            for (int i = 0; i < endoffset; i++) {
                m_contents += "??";
            }
            QStringList list;
            for (int i = 0; i <= (m_showBytes-1)/16; i++) {
                QString tmpAddr = QString::number(first + 16*i, 16);
                int zeroNum = 8-tmpAddr.size();
                for (int j = 0; j < zeroNum; j++) {
                    tmpAddr.push_front('0');
                }
                tmpAddr.push_front("0x");
                QString tmpContents = m_contents.mid(i*32, 32);
                QString ascii;
                bool ok;
                char c;
                for (int j = 0; j < 16; j++) {
                    c = static_cast<char>(tmpContents.mid(2*j, 2).toInt(&ok, 16));
                    if (c < 32 || c > 126) c = 'x';
                    ascii.append(c);
                }
                ascii.insert(8, " ");
                for (int k = 0; k < 15; k++) {
                    tmpContents.insert((k+1)*2+k, " ");
                }
                tmpContents.insert(23, " ");
                if (i == m_showBytes/16)
                    tmpContents.insert(23, "  ");
                list.append(
                            tmpAddr +
                            "    " + tmpContents + "    " + ascii);

                memToShow += list.at(i);
                if (i < (m_showBytes-1)/16)
                    memToShow += "\n";
            }
            m_memBrowser->setText(memToShow);
        }
    } else {
        MIResult *result = resultRecord->m_pResult;
        QString addr;
        QString memToShow = "";
        while (result) {
            qDebug(QString(result->m_variable).toUtf8().constData());
            if (!QString::compare(result->m_variable, "addr")) {
                addr = result->m_pValue->m_string;
            } else if (!QString::compare(result->m_variable, "memory")) {
                MIValue *lineValue = result->m_pValue->m_pList->m_pValue;
                while (lineValue) {
                    qDebug(QString(lineValue->m_type).toUtf8().constData());
                    MIResult *lineResult = lineValue->m_pResult;
                    while (lineResult) {
                        if (!QString::compare(lineResult->m_variable, "addr")) {
                            memToShow.append(lineResult->m_pValue->m_string);
                            memToShow.append("    ");
                            //lineAddr = result->m_pValue->m_string;
                        }
                        else if (!QString::compare(lineResult->m_variable, "data")) {
                            MIValue *lineValue = lineResult->m_pValue->m_pList->m_pValue;
                            int count = 0;
                            while (lineValue) {
                                memToShow.append(QString(lineValue->m_string).mid(2));
                                memToShow.append(" ");
                                count++;
                                if (count % 8 == 0) {
                                    memToShow.append(" ");
                                }
                                lineValue = lineValue->m_pNext;
                            }
                        }
                        else if (!QString::compare(lineResult->m_variable, "ascii")) {
                            memToShow.append("   ");
                            QString ascii(lineResult->m_pValue->m_string);
                            ascii.insert(8, " ");
                            memToShow.append(ascii);
                            memToShow.append("\n");
                        }
                        lineResult = lineResult->m_pNext;
                    }
                    lineValue = lineValue->m_pNext;
                }
            }
            result = result->m_pNext;
        }
        m_memBrowser->setText(memToShow);
    }
    return false;
}

bool GDBMIMemoryClient::showMemory () {
    QString address = m_addrLine->text();
    QString bytes = m_bytesLine->text();
    if (address.isEmpty() || bytes.isEmpty()) {
        m_memBrowser->setText(QObject::tr("Neither address nor length could be empty"));
        return false;
    }
    int bytesNum = bytes.toInt();
    if (bytesNum == 0) {
        m_memBrowser->setText(QObject::tr("The length cannot be zero"));
        return false;
    }
    int lineNum = bytesNum/16;
    if (bytesNum%16 != 0) lineNum++;
    bytesNum = lineNum*16;
    if (m_pGDBMI->verIsNew()) {
        sendRequest(QString("-data-read-memory-bytes %1 %2").arg(address).arg(bytesNum));
    } else {
        sendRequest(QString("-data-read-memory %1 x 1 %2 16 x").arg(address).arg(lineNum));
    }
    return true;
}

void GDBMIMemoryClient::onResultParseErr(int /*token*/) {
    return;
}

void GDBMIMemoryClient::onStarted() {
}
